今天我們來說一下 NautilusTrader 是如何 結合 Rust 和 Python 的
可以透過查看 build.py 來得到,步驟如下
Rust 函式庫是使用 Cargo 編譯的,具有特定的功能標誌和最佳化設定檔。
意思是:你用 Rust 寫好程式後,會透過 cargo build 編譯出 .a(靜態庫)或 .so/.dylib/.dll(動態庫)。
這一段可以參考 build.py:146-194
支援 FFI 的 Rust crate 使用 cbindgen 產生 C 標頭,然後 Cython 將其用於 FFI 整合。
因為 Cython 無法直接使用 Rust 的函式定義,它只能識別 C 的函式簽名。所以用 cbindgen 把 Rust 的函式輸出成 .h 檔:
Cython 擴充功能使用 NumPy 整合進行編譯,並與 Rust 靜態函式庫連結。
這表示你在寫 Cython 的 .pyx 時可能也用到 NumPy(如資料傳遞),而最重要的是:
這一段可以參考 build.py:279-291
PyO3 共享函式庫被建置並複製到 Python 套件結構中。
如果不是用 C header + Cython 的方式,而是使用 PyO3 這種更現代的整合方式,Rust 可以直接 export 為 Python 模組(不需要 C header),也是我們在 Day 5 所講的.
這會產生一個 .so 檔案(Python 模組),並複製到你開發的 Python 專案中。
這一段可以參考 build.py:331-338
建置的延伸會複製回原始碼樹狀結構,以進行開發和輪子封裝。
意思是:上面用 maturin build 或 setup.py build_ext 產生的 .so / .pyd 模組,會被放回原始的 Python 套件結構中,像這樣:
project/
├── my_module/
│ ├── __init__.py
│ ├── my_module.cpython-38-x86_64-linux-gnu.so <-- Rust/Cython 編譯結果
這樣 Python 就可以直接 import my_module 使用 Rust 加速過的模組了。
這一段可以參考 build.py:315-328
所以根據以上,我們知道有兩種方式
情境一:原本就有 Python + Cython 結構
Python ← Cython (.pyx) ← C header (.h) ← Rust
情境二:只有純 Python 架構
Python ← PyO3 ← Rust
所以我們自己想開發的話,應該要使用第二種方法會比較容易,明天會來說明一下,預計要開發什麼新的 crate.